package com.test.aop;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.List;

/**
 * 使用CGLIB实现的AopProxy
 * CGLIB是一个强大的高性能的代码生成库，它可以在运行期扩展Java类与实现Java接口
 * 该类主要用于创建目标类的子类，从而实现代理功能
 */
public class CglibAopProxy implements AopProxy{
    private final AdvisedSupport advised;

    /**
     * 构造方法，初始化CglibAopProxy对象
     *
     * @param advised 包含代理配置信息的对象
     */
    public CglibAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }

    /**
     * 获取代理对象
     *
     * @return 代理对象
     */
    @Override
    public Object getProxy() {
        return getProxy(null);
    }

    /**
     * 使用指定的类加载器获取代理对象
     *
     * @param classLoader 类加载器，用于加载代理类
     * @return 代理对象
     * @throws IllegalStateException 如果目标类为空
     */
    @Override
    public Object getProxy(ClassLoader classLoader) {
        if (this.advised.getTargetSource() == null){
            throw new IllegalStateException("TargetSource不能为空");
        }
        Class<?> targetClass = advised.getTargetSource().getTargetClass();
        if (targetClass == null){
            throw new IllegalStateException("目标类不能为空");
        }
        Enhancer enhancer = new Enhancer();
        if (classLoader != null){
            enhancer.setClassLoader(classLoader);
        }
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(new CglibMethodInterceptor());
        return enhancer.create();
    }

    /**
     * CGLIB方法拦截器，用于处理方法调用
     */
    private class CglibMethodInterceptor implements MethodInterceptor {
        /**
         * 拦截方法调用
         *
         * @param o          代理对象
         * @param method     被调用的方法
         * @param args       方法参数
         * @param methodProxy 方法代理对象，用于调用目标方法
         * @return 方法返回值
         * @throws Throwable 如果方法调用过程中出现异常
         */
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            Object target = null;
            try{
                target = advised.getTargetSource().getTarget();
                if (target == null){
                    throw new IllegalStateException("目标对象不能为空");
                }

                List<Object> chain = advised.getInterceptorsAndDynamicInterceptionAdvice(method, target.getClass());

                if (chain.isEmpty()){
                    return method.invoke(target, args);
                }

                CglibMethodInvocation invocation = new CglibMethodInvocation(target, method, args, methodProxy);
                return processInterceptors(chain, invocation);
            }finally {
                if (target != null){
                    advised.getTargetSource().releaseTarget(target);
                }
            }
        }
    }

    /**
     * CGLIB方法调用对象，用于处理方法调用
     */
    private static class CglibMethodInvocation extends ReflectiveMethodInvocation{
        private final MethodProxy methodProxy;

        /**
         * 构造方法，初始化ReflectiveMethodInvocation对象
         *
         * @param target    目标对象，即方法所属的实例
         * @param method    要调用的方法
         * @param arguments 方法的参数
         */
        public CglibMethodInvocation(Object target, Method method, Object[] arguments, MethodProxy methodProxy) {
            super(target, method, arguments);
            this.methodProxy = methodProxy;
        }

        /**
         * 调用连接点方法
         *
         * @return 方法返回值
         * @throws Throwable 如果方法调用过程中出现异常
         */
        @Override
        protected Object invokeJoinPoint() throws Throwable {
            return methodProxy.invoke(getThis(), getArguments());
        }
    }

    /**
     * 处理拦截器链
     *
     * @param chain     拦截器链，包含所有需要执行的拦截器
     * @param invocation 方法调用对象，用于执行方法和获取方法信息
     * @return 方法返回值
     * @throws Throwable 如果方法调用过程中出现异常
     */
    private Object processInterceptors(List<Object> chain, ReflectiveMethodInvocation invocation) throws Throwable {
        Object returnValue = null;
        for (Object advice : chain) {
            if (advice instanceof MethodBeforeAdvice) {
                ((MethodBeforeAdvice) advice).before(invocation.getMethod(), invocation.getArguments(), invocation.getThis());
            }
        }
        returnValue = invocation.proceed();
        for (Object advice : chain) {
            if (advice instanceof AfterReturningAdvice) {
                ((AfterReturningAdvice) advice).afterReturning(returnValue, invocation.getMethod(), invocation.getArguments(), invocation.getThis());
            }
        }
        return returnValue;
    }
}
